/*
函数
*/

package main

import (
	"errors"
	"fmt"
	"math"
)

/* 定义结构体 */
type Circle struct {
	radius float64
}

/*
go语言中init函数用于包(package)的初始化，该函数是go语言的一个重要特性。

有下面的特征：
    1 init函数是用于程序执行前做包的初始化的函数，比如初始化包里的变量等
    2 每个包可以拥有多个init函数
    3 包的每个源文件也可以拥有多个init函数
    4 同一个包中多个init函数的执行顺序go语言没有明确的定义(说明)
    5 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
    6 init函数不能被其他函数调用，而是在main函数执行之前，自动被调用
*/
func init() {
	fmt.Println("=====init1=====")
}
func init() {
	fmt.Println("=====init2=====")
}

func main() {
	fmt.Println("=====值传递与引用传递=====")
	delivery()

	fmt.Println("-----函数作为实参------")

	// 函数作为变量
	getSquareRoot := func(x float64) float64 {
		return math.Sqrt(x)
	}
	fmt.Println(getSquareRoot(9))

	fmt.Println("-----函数方法------")
	var c1 Circle
	c1.radius = 10.00
	fmt.Println("圆的面积 = ", c1.getArea())

	ax, bx := ab(1)
	fmt.Println("ax:", ax, " bx:", bx)
	ax, bx = ab(-1)
	fmt.Println("ax:", ax, " bx:", bx)

	fmt.Println("=====闭包=====")
	closure()
	// Go 语言支持匿名函数，可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量，不必申明。
	/* nextNumFun1 为一个函数，函数 i 为 0 */
	fmt.Println("nextNumFun1")
	nextNumFun1 := nextNum()
	fmt.Println(nextNumFun1())
	fmt.Println(nextNumFun1())
	fmt.Println(nextNumFun1())

	/* 创建新的函数 nextNumFun，并查看结果 */
	fmt.Println("nextNumFun2")
	nextNumFun2 := nextNum()
	fmt.Println(nextNumFun2())
	fmt.Println(nextNumFun2())

	fmt.Println("=====可变参数函数=====")
	parameters()
}

func delivery() {
	/* 定义局部变量 */
	var a int = 100
	var b int = 200

	fmt.Printf("交换前 a 的值为 : %d\n", a)
	fmt.Printf("交换前 b 的值为 : %d\n", b)

	/* 通过调用函数来交换值 */
	swap(a, b)

	fmt.Printf("交换后 a 的值 : %d\n", a)
	fmt.Printf("交换后 b 的值 : %d\n", b)

	/* 调用 swap2() 函数
	 * &a 指向 a 指针，a 变量的地址
	 * &b 指向 b 指针，b 变量的地址
	 */
	swap2(&a, &b)

	fmt.Printf("交换后 a 的值 : %d\n", a)
	fmt.Printf("交换后 b 的值 : %d\n", b)
}

// 值传递
func swap(x, y int) int {
	var temp int

	temp = x /* 保存 x 的值 */
	x = y    /* 将 y 值赋给 x */
	y = temp /* 将 temp 值赋给 y*/

	return temp
}

// 引用传递
func swap2(x *int, y *int) {
	var temp int
	temp = *x /* 保存 x 地址上的值 */
	*x = *y   /* 将 y 值赋给 x */
	*y = temp /* 将 temp 值赋给 y */
}

// 闭包
func nextNum() func() int {
	i := 0
	return func() int {
		i += 1
		return i
	}
}

// 该 method 属于 Circle 类型对象中的方法
// func (variable_name variable_data_type) function_name() [return_type]
func (c Circle) getArea() float64 {
	//c.radius 即为 Circle 类型对象中的属性
	return 3.14 * c.radius * c.radius
}

func ab(n int) (int, error) {
	if n < 0 {
		return 0, errors.New("n < 0")
	}
	return 1, nil
}

/*
闭包
https://www.topgoer.com/函数/闭包递归.html
闭包是由函数及其相关引用环境组合而成的实体(即：闭包=函数+引用环境)。

输出
1
2
3
*/
func closure() {
	c := closureA()

	c()
	c()
	c()

	closureA()
}

func closureA() func() int {
	i := 0
	b := func() int {
		i++
		fmt.Println(i)
		return i
	}
	return b
}

func parameters() {
	a, b, c := 1, 2, 3
	fmt.Println(sum(a, b, c))

	nums := []int{1, 2, 3}
	fmt.Println(sum(nums...))
}

func sum(num ...int) int {
	var s = 0
	for _, v := range num {
		s += v
	}
	return s
}
